package be.kdg.bombermanunlimited.audio.rmi;

import java.rmi.AccessException;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.util.Timer;
import java.util.TimerTask;

import be.kdg.bombermanunlimited.audio.player.AudioPlayer;
import be.kdg.bombermanunlimited.audio.server.AudioServer;
import be.kdg.bombermanunlimited.client.CentralRegistry;
import be.kdg.bombermanunlimited.client.CentralRegistry.BombermanObjects;
import be.kdg.bombermanunlimited.client.network.listeners.INetworkListener;
import be.kdg.bombermanunlimited.server.audiomanager.IAudioManager;

public class LocalAudioManager implements Runnable, INetworkListener {

	/**
	 * Deze klasse controleert de audioplayer en de audioserver. De klasse staat
	 * eveneens in verbinding met de centrale audioserver zodat deze de
	 * audiomanager kan contacteren wanneer de audioserver gestart moet worden.
	 * 
	 * @author Siegfried
	 */
	// De IAudioManager voor de verbinding met de server.
	private IAudioManager remoteManager;
	private AudioServer server;
	private AudioPlayer player;
	private boolean connected = true;
	private byte id = 0;

	/**
	 * 
	 * @param managerIp
	 *            IP van de centrale server
	 * @param managerPort
	 *            poort van de centrale server
	 * @throws RemoteException
	 *             Wanneer er problemen zijn met de verbinding met de centrale
	 *             server.
	 */
	public LocalAudioManager(String managerIp, int managerPort)
			throws RemoteException {
		try {
			remoteManager = (IAudioManager) LocateRegistry.getRegistry(
					managerIp, managerPort).lookup("/AudioManager");
		} catch (AccessException e) {
			e.printStackTrace();
		} catch (RemoteException e) {
			e.printStackTrace();
		} catch (NotBoundException e) {
			e.printStackTrace();
		}
		if (remoteManager == null) {
			// System.out
			// .println("[NetworkFactory] Couldn't connect with the manager!");
		}
		// start de audioplayer.
		if (player == null) {
			player = new AudioPlayer();
			player.start();
		}
		// maak de audioserver aan.
		server = new AudioServer();
		CentralRegistry reg = CentralRegistry.getRegistry();
		// Registreer de remote manager als AudioListener.
		remoteManager.addAudioListener(
				reg.getString(BombermanObjects.LOCAL_IP),
				reg.getInteger(BombermanObjects.RING_PORT),
				server.isServerEnabled(), new AudioListener());
		// player.addAudioPlayerListener(this);
	}

	/**
	 * PlayBackCheck is een TimerTask die in loop wordt uitgevoerd tot de
	 * audioplayer klaar is met afspelen. Wanneer dit het geval is wordt de
	 * audioserver gestart. Indien er iets misloopt met het ringnetwerk, zal de
	 * manager proberen de centrale server te contacteren om zijn rol als
	 * audioserver te weigeren.
	 * 
	 */
	class PlaybackCheck extends TimerTask {
		public void run() {
			// System.out.println("[LocalAudioManager] Timer check");
			CentralRegistry reg = CentralRegistry.getRegistry();
			try {
				if (!connected) {

					remoteManager.declineServerRole();

					// System.out.println("Server Role Declined");
					this.cancel();
				}
				// Requesttotransmit is een extra controle. De manager vraagt
				// aan de centrale server of hij wel degelijk mag starten.
				if (!remoteManager.requestToTransmit(
						reg.getString(BombermanObjects.LOCAL_IP),
						reg.getInteger(BombermanObjects.RING_PORT),
						reg.getString(BombermanObjects.USERNAME))) {
					// to make sure we don't launch a server that isn't allowed
					// to start.
					// System.out
					// .println("Request for transmission of audio refused. Terminate timer.");
					this.cancel();
				} else if (!player.isStillPlaying()) {
					// System.out
					// .println("Request for transmission of audio granted. Start playback.");
					new Thread(LocalAudioManager.this).start();
					this.cancel();
				}
				// System.out.println("Still playing...."
				// + player.getBufferDataLength());
			} catch (RemoteException e) {
				e.printStackTrace();
			}

		}
	}

	public void notifyChange(int value) {
		System.out.println("Client notified new value " + value);
	}

	/**
	 * Dit zijn de instructies die door de centrale server kunnen worden
	 * opgeroepen.
	 */
	public class AudioListener extends java.rmi.server.UnicastRemoteObject
			implements IAudioListener {

		private static final long serialVersionUID = -4710553394853634056L;

		public AudioListener() throws java.rmi.RemoteException {
		}

		public void valueChanged(int value) throws java.rmi.RemoteException {
			notifyChange(value);
		}

		@Override
		public boolean IsUp() throws RemoteException {
			return true;
		}

		/**
		 * Start een timer. Zodra het afspelen van audio door de audioplayer is
		 * voltooid zal deze timer de audioserver starten.
		 * 
		 * @param id
		 *            het id dat de centrale server aan deze audioserver
		 *            meegeeft. Dit id wordt bij elk audio data pakket mee
		 *            verzonden ter verificatie.
		 */
		@Override
		public synchronized void startPlaying(byte id) throws RemoteException {
			// System.out
			// .println("[AudioManager]Request to become audio server received. Wait for transmission to end.");
			LocalAudioManager.this.id = id;
			if (!connected) {
				// System.err
				// .println("Error: Can't become server. Not connected to ring.");
				try {
					remoteManager.declineServerRole();
				} catch (Exception e) {

				}
			} else {
				// System.out.println("Starting timer");
				Timer timer = new Timer(true);
				timer.schedule(new PlaybackCheck(), 200, 1 * 200);
			}
		}

		// Flush buffer wist alle audio die nog in de circulaire buffer van de
		// audioplayer staat en zorgt ervoor dat alle pakketten van de vorige
		// huidige server geweigerd worden.
		@Override
		public void flushBuffer() throws RemoteException {
			player.flush();
		}
	}

	/**
	 * Test of de manager toelating heeft om de audioserver te starten. Indien
	 * dit het geval is wordt de stream gestart. Na het afronden van de stream
	 * wordt een End of File melding naar de centrale server gestuurd.
	 */
	@Override
	public void run() {
		CentralRegistry reg = CentralRegistry.getRegistry();
		try {
			if (remoteManager.requestToTransmit(
					reg.getString(BombermanObjects.LOCAL_IP),
					reg.getInteger(BombermanObjects.RING_PORT),
					reg.getString(BombermanObjects.USERNAME))) {
				server.startStream(id);

				// System.out.println("[AudioManager] EOF");

				remoteManager.endOfFile(
						reg.getString(BombermanObjects.LOCAL_IP),
						reg.getInteger(BombermanObjects.RING_PORT));

			} else {
				// System.out.println("Error: request to transmit denied");
			}
		} catch (RemoteException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	// muten van de audio player.
	public void mute(boolean mute) {
		player.mute(mute);
	}

	@Override
	public void onConnect() {

	}

	@Override
	public void onDisconnect() {
		server.stop();
	}

	// indien verbinding met de ring verloren gaat, wordt connected op false
	// gezet. Als dit het geval is zal de manager aanvragen vanaf de centrale
	// server voor het starten van de audioserver weigeren.
	@Override
	public void onConnectionLost() {
		connected = false;
	}

	@Override
	public void nodeAdded() {
		connected = true;
	}
}
